package org.springblade.gateway.filter;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.constant.AppConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
import org.springframework.cloud.netflix.ribbon.*;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;

/**
 * @Author: liw
 * @Date: 2020/11/2 19:56
 */
@Slf4j
@Component
public class BladeLoadBalancerClientFilter extends LoadBalancerClientFilter {

	@Autowired
	private SpringClientFactory clientFactory;


//	static List<String> BASE_SERVICE_NAME = new ArrayList<>();
//
//	{
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_GATEWAY_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_AUTH_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_ADMIN_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_TURBINE_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_ZIPKIN_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_WEBSOCKET_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_DESK_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_SYSTEM_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_USER_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_LOG_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_DEVELOP_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_FLOWDESIGN_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_FLOW_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_RESOURCE_NAME);
//		BASE_SERVICE_NAME.add(AppConstant.APPLICATION_TEST_NAME);
//		BASE_SERVICE_NAME.add("ams");
//	}


	public BladeLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
		super(loadBalancer, properties);
	}

	@Override
	protected ServiceInstance choose(ServerWebExchange exchange) {
		ConfigurableEnvironment environment = new StandardEnvironment();
		String[] activeProfiles = environment.getActiveProfiles();
		// dev 环境
		if ("dev".equalsIgnoreCase(activeProfiles[0])) {
			String serviceId = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
			ILoadBalancer loadBalancer = clientFactory.getLoadBalancer(serviceId);
			List<Server> allServers = loadBalancer.getAllServers();
			// 基础服务 只会调用服务器端公用的服务
//			if (BASE_SERVICE_NAME.contains(serviceId)) {
//				return super.choose(exchange);
//			}
			// 不是TMS开头的项目 全部都调用服务器端公用的服务
			if (!(serviceId.contains("tms"))) {
				return super.choose(exchange);
			}
			// 请求头中 指定有ip 找指定ip 找不到 报错
			if (exchange.getRequest().getHeaders().containsKey("service-ip")) {
				String serviceHost = exchange.getRequest().getHeaders().get("service-ip").get(0);
				log.info("当前header中service-ip:{}", serviceHost);
				ServiceInstance serviceInstance = getServiceInstance(serviceId, allServers, serviceHost);
				if (serviceInstance != null) {
					return serviceInstance;
				} else {
					throw new RuntimeException("当前指定请求ip:" + serviceHost + ",未启动对应服务实例:" + serviceId);
				}
			} else {
				// 沒有指定ip 优先本地 本地 没有 找开发环境非192开头的服务
				String apiHost = getIpAddress(exchange.getRequest());
				log.info("当前请求机器ip: apiHost:{}", apiHost);
				ServiceInstance serviceInstance = getServiceInstance(serviceId, allServers, apiHost);
				if (serviceInstance != null) {
					return serviceInstance;
				}
				Optional<Server> first = allServers.stream().filter(server -> !server.getHost().startsWith("192")).findFirst();
				if (first.isPresent()) {
					Server server = first.get();
					return new RibbonLoadBalancerClient.RibbonServer(serviceId, server, isSecure(server, serviceId),
						serverIntrospector(serviceId).getMetadata(server));
				}
			}

		}
		return super.choose(exchange);
	}

	private ServiceInstance getServiceInstance(String serviceId, List<Server> allServers, String apiHost) {
		Optional<Server> first = allServers.stream().filter(server -> server.getHost().equalsIgnoreCase(apiHost)).findFirst();
		if (first.isPresent()) {
			Server server = first.get();
			return new RibbonLoadBalancerClient.RibbonServer(serviceId, server, isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));
		}
		return null;
	}


	private boolean isSecure(Server server, String serviceId) {
		IClientConfig config = this.clientFactory.getClientConfig(serviceId);
		ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
		return RibbonUtils.isSecure(config, serverIntrospector, server);
	}


	private ServerIntrospector serverIntrospector(String serviceId) {
		ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,
			ServerIntrospector.class);
		if (serverIntrospector == null) {
			serverIntrospector = new DefaultServerIntrospector();
		}
		return serverIntrospector;
	}


	public static String getIpAddress(ServerHttpRequest request) {
		HttpHeaders headers = request.getHeaders();
		String ip = headers.getFirst("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = headers.getFirst("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = headers.getFirst("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = headers.getFirst("X-Real-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddress().getAddress().getHostAddress();
		}
		if (ip != null && ip.length() > 15 && ip.contains(",")) {
			ip = ip.substring(0, ip.indexOf(","));
		}
		return ip;
	}


}

